﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Principal;
using System.Reflection;
using System.Diagnostics;
using System.Net.Mail;
using System.Net;

namespace TFSAutoShelve
{
    /// <summary>
    /// A utility class for sending error reports.
    /// </summary>
    internal static class ErrorReport
    {
        /// <summary>
        /// Sends an error report using the supplied parameters.
        /// </summary>
        /// <param name="exception">The exception.</param>
        /// <param name="fromAddress">Error report from address.</param>
        /// <param name="toEmailAddress">Error report to email address.</param>
        /// <param name="ccEmailAddressList">Error report cc email address list.</param>
        /// <param name="smtpHostName">SMTP host used to send the error report.</param>
        internal static void SendErrorReport(
            Exception exception, 
            string fromAddress, 
            string toEmailAddress, 
            string ccEmailAddressList,
            string smtpHostName)
        {
            // Logging info
            Trace.WriteLine(Properties.Resources.MSG_SENDING_ERR);
            
            // Create the mail message
            MailMessage mm = new MailMessage();

            // Set the from address
            mm.From = new MailAddress(fromAddress);

            // Set the to address
            mm.To.Add(toEmailAddress);

            // Add the CC addresses
            if (!string.IsNullOrEmpty(ccEmailAddressList))
            {
                mm.CC.Add(ccEmailAddressList);
            }

            // Set the subject and body
            mm.Subject = Properties.Resources.ERR_NOTIFICATION_SUBJECT;
            mm.Body = FormatException(exception);

            // Send the message
            SmtpClient sc = new SmtpClient(smtpHostName);
            sc.Credentials = CredentialCache.DefaultNetworkCredentials;
            sc.Send(mm);

        } // end SendErrorReport()
        
        /// <summary>
        /// Formats the exception.
        /// </summary>
        /// <param name="exception">The exception.</param>
        /// <returns>A string containing formatted exception information.</returns>
        internal static string FormatException(Exception exception)
        {
            const string TEXT_SEPARATOR = "----------------------------------------------------------------------";

            // Create StringBuilder to maintain publishing information.
            StringBuilder errRpt = new StringBuilder();

            errRpt.AppendFormat("{0}{1}{0}", Environment.NewLine, Properties.Resources.ERR_NOTIFICATION_LEADIN);
            errRpt.AppendFormat("{0}{0}Exception Details {0}{1}", Environment.NewLine, TEXT_SEPARATOR);

            if (exception == null)
            {
                errRpt.AppendFormat("{0}{0}No Exception object has been provided.{0}", Environment.NewLine);
            }
            else
            {
                // Loop through each exception class in the chain of exception objects.
                Exception currentException = exception;	// Temp variable to hold InnerException object during the loop.
                int intExceptionCount = 1;				// Count variable to track the number of exceptions in the chain.
                do
                {
                    // Write title information for the exception object.
                    errRpt.AppendFormat("{0}{0}{1}) Exception Information{0}{2}", Environment.NewLine, intExceptionCount, TEXT_SEPARATOR);
                    errRpt.AppendFormat("{0}Exception Type: {1}", Environment.NewLine, currentException.GetType().FullName);

                    // Loop through the public properties of the exception object and record their value.
                    PropertyInfo[] aryPublicProperties = currentException.GetType().GetProperties();

                    foreach (PropertyInfo p in aryPublicProperties)
                    {
                        // Do not log information for the InnerException or StackTrace. This information is 
                        // captured later in the process.
                        if (p.Name != "InnerException" && p.Name != "StackTrace")
                        {
                            try
                            {
                                if (p.GetValue(currentException, null) == null)
                                {
                                    errRpt.AppendFormat("{0}{1}: NULL", Environment.NewLine, p.Name);
                                }
                                else
                                {
                                    errRpt.AppendFormat("{0}{1}: {2}", Environment.NewLine, p.Name, p.GetValue(currentException, null));
                                }
                            }
                            catch
                            {
                                // Ignore
                            }
                        }
                    }

                    // Record the StackTrace with separate label.
                    if (currentException.StackTrace != null)
                    {
                        errRpt.AppendFormat("{0}{0}StackTrace Information{0}{1}", Environment.NewLine, TEXT_SEPARATOR);
                        errRpt.AppendFormat("{0}{1}", Environment.NewLine, currentException.StackTrace);
                    }

                    // Reset the temp exception object and iterate the counter.
                    currentException = currentException.InnerException;
                    intExceptionCount++;

                } while (currentException != null);

            }

            // Write Environment information
            errRpt.AppendFormat("{0}{0}Environment Information{0}{1}", Environment.NewLine, TEXT_SEPARATOR);
            errRpt.AppendFormat("{0}{1}: {2}", Environment.NewLine, "Date", DateTime.Now);
            errRpt.AppendFormat("{0}{1}: {2}", Environment.NewLine, "TFS Server", Properties.Settings.Default.TFSServer);
            errRpt.AppendFormat("{0}{1}: {2}", Environment.NewLine, ".NET Version", Environment.Version);
            errRpt.AppendFormat("{0}{1}: {2}", Environment.NewLine, "OS Version", Environment.OSVersion);
            errRpt.AppendFormat("{0}{1}: {2}", Environment.NewLine, "CurrentDirectory", Environment.CurrentDirectory);
            errRpt.AppendFormat("{0}{1}: {2}", Environment.NewLine, "MachineName", Environment.MachineName);
            errRpt.AppendFormat("{0}{1}: {2}", Environment.NewLine, "UserDomainName", Environment.UserDomainName);
            errRpt.AppendFormat("{0}{1}: {2}", Environment.NewLine, "UserName", Environment.UserName);

            WindowsIdentity identity = WindowsIdentity.GetCurrent();

            // Write Identity information
            errRpt.AppendFormat("{0}{0}Identity Information{0}{1}", Environment.NewLine, TEXT_SEPARATOR);
            errRpt.AppendFormat("{0}{1}: {2}", Environment.NewLine, "UserName", identity.Name);
            errRpt.AppendFormat("{0}{1}: {2}", Environment.NewLine, "IsSystem", identity.IsSystem);
            errRpt.AppendFormat("{0}{1}: {2}", Environment.NewLine, "IsAuthenticated", identity.IsAuthenticated);
            errRpt.AppendFormat("{0}{1}: {2}", Environment.NewLine, "AuthenticationType", identity.AuthenticationType);
            errRpt.AppendFormat("{0}{1}: {2}", Environment.NewLine, "Token", identity.Token);

            errRpt.AppendFormat("{0}{0}{0}", Environment.NewLine);

            return errRpt.ToString();

        } // end FormatException() 
    }
}
